"""
通知服务模块
用于发送各种类型的通知消息
"""
import hashlib
import hmac
import base64
import time
import requests
import json
from datetime import datetime
from services.shared.models import NotificationConfig
from services.shared.logger import get_logger
from services.shared.log_utils import log_error, log_info, log_system_event

logger = get_logger('shared.notification')

class NotificationService:
    """通知服务类"""
    
    @staticmethod
    def generate_sign(secret, timestamp):
        """生成钉钉机器人加签"""
        string_to_sign = f'{timestamp}\n{secret}'
        hmac_code = hmac.new(
            secret.encode('utf-8'),
            string_to_sign.encode('utf-8'),
            digestmod=hashlib.sha256
        ).digest()
        sign = base64.b64encode(hmac_code).decode('utf-8')
        return sign
    
    @staticmethod
    def send_dingtalk_message(webhook_url, secret, title, content):
        """发送钉钉消息"""
        logger.info(f"开始发送钉钉消息: {title}")
        logger.debug(f"钉钉消息详情 - URL: {webhook_url[:50]}..., 标题: {title}, 内容长度: {len(content)}")
        
        try:
            # 构建消息体
            message = {
                "msgtype": "markdown",
                "markdown": {
                    "title": title,
                    "text": content
                }
            }
            
            # 如果有加签密钥，添加签名参数
            url = webhook_url
            if secret:
                timestamp = str(round(time.time() * 1000))
                sign = NotificationService.generate_sign(secret, timestamp)
                
                # 添加签名参数
                separator = '&' if '?' in url else '?'
                url = f"{url}{separator}timestamp={timestamp}&sign={sign}"
                logger.debug("已添加钉钉签名参数")
            
            # 发送请求
            headers = {'Content-Type': 'application/json'}
            logger.debug("开始发送HTTP请求到钉钉")
            response = requests.post(
                url,
                data=json.dumps(message),
                headers=headers,
                timeout=10
            )
            
            if response.status_code == 200:
                result = response.json()
                if result.get('errcode') == 0:
                    logger.info(f"钉钉消息发送成功: {title}")
                    log_info(
                        "notification_dingtalk_success",
                        f"钉钉消息发送成功: {title}",
                        details=json.dumps({
                            "title": title,
                            "content_length": len(content),
                            "webhook_url": webhook_url[:50] + "...",
                            "has_secret": bool(secret)
                        }, ensure_ascii=False)
                    )
                    return True, '发送成功'
                else:
                    error_msg = result.get('errmsg', '未知错误')
                    logger.error(f"钉钉消息发送失败: {error_msg}")
                    log_error(
                        "notification_dingtalk_api_error",
                        f"钉钉API返回错误: {error_msg}",
                        details=json.dumps({
                            "title": title,
                            "errcode": result.get('errcode'),
                            "errmsg": error_msg,
                            "webhook_url": webhook_url[:50] + "..."
                        }, ensure_ascii=False)
                    )
                    return False, error_msg
            else:
                error_msg = f'HTTP错误：{response.status_code}'
                logger.error(f"钉钉消息发送HTTP错误: {error_msg}")
                log_error(
                    "notification_dingtalk_http_error",
                    f"钉钉消息发送HTTP错误: {error_msg}",
                    details=json.dumps({
                        "title": title,
                        "status_code": response.status_code,
                        "response_text": response.text[:200],
                        "webhook_url": webhook_url[:50] + "..."
                    }, ensure_ascii=False)
                )
                return False, error_msg
                
        except requests.exceptions.Timeout:
            error_msg = '请求超时'
            logger.error(f"钉钉消息发送超时: {title}")
            log_error(
                "notification_dingtalk_timeout",
                f"钉钉消息发送超时: {title}",
                details=json.dumps({
                    "title": title,
                    "webhook_url": webhook_url[:50] + "...",
                    "timeout": 10
                }, ensure_ascii=False)
            )
            return False, error_msg
        except requests.exceptions.RequestException as e:
            error_msg = f'网络错误：{str(e)}'
            logger.error(f"钉钉消息发送网络错误: {error_msg}")
            log_error(
                "notification_dingtalk_network_error",
                f"钉钉消息发送网络错误: {title}",
                details=json.dumps({
                    "title": title,
                    "error": str(e),
                    "webhook_url": webhook_url[:50] + "..."
                }, ensure_ascii=False)
            )
            return False, error_msg
        except Exception as e:
            error_msg = f'发送失败：{str(e)}'
            logger.error(f"钉钉消息发送异常: {error_msg}")
            log_error(
                "notification_dingtalk_exception",
                f"钉钉消息发送异常: {title}",
                details=json.dumps({
                    "title": title,
                    "error": str(e),
                    "webhook_url": webhook_url[:50] + "..."
                }, ensure_ascii=False)
            )
            return False, error_msg
    
    @staticmethod
    def send_task_failure_notification(task, execution_record, notification_config):
        """发送任务失败通知"""
        try:
            # 构建通知内容
            title = f"任务执行失败：{task.name}"
            
            # 格式化执行时间
            start_time = execution_record.started_at.strftime('%Y-%m-%d %H:%M:%S') if execution_record.started_at else '未知'
            end_time = execution_record.completed_at.strftime('%Y-%m-%d %H:%M:%S') if execution_record.completed_at else '未知'
            
            # 构建Markdown格式的消息内容
            content = f"""## 🚨 任务执行失败通知

**任务名称：** {task.name}

**任务ID：** {task.id}

**执行时间：** {start_time} - {end_time}

**失败原因：**
```
{execution_record.error_message or '未知错误'}
```

**任务描述：** {task.description or '无'}

**脚本文件：** {task.script_file_path or '用户脚本'}

**工作目录：** {task.working_directory or '默认'}

---
*此消息由XbyJob自动发送*
"""
            
            # 发送通知
            success, message = NotificationService.send_dingtalk_message(
                webhook_url=notification_config.webhook_url,
                secret=notification_config.secret,
                title=title,
                content=content
            )
            
            if success:
                logger.info(f"任务失败通知发送成功：任务ID={task.id}, 配置ID={notification_config.id}")
            else:
                logger.error(f"任务失败通知发送失败：任务ID={task.id}, 配置ID={notification_config.id}, 错误={message}")
            
            return success, message
            
        except Exception as e:
            error_msg = f"发送任务失败通知时出错：{str(e)}"
            logger.error(error_msg)
            return False, error_msg
    
    @staticmethod
    def send_task_failure_notification_by_config_id(task, execution_record, config_id):
        """通过配置ID发送任务失败通知"""
        try:
            # 获取通知配置 - 使用数据库会话而不是Flask-SQLAlchemy的query
            from services.shared.db_pool import get_db_session
            with get_db_session() as session:
                notification_config = session.query(NotificationConfig).filter_by(
                    id=config_id,
                    is_enabled=True
                ).first()
                
                if not notification_config:
                    logger.warning(f"通知配置不存在或已禁用：配置ID={config_id}")
                    return False, "通知配置不存在或已禁用"
                
                # 发送通知
                return NotificationService.send_task_failure_notification(
                    task, execution_record, notification_config
                )
            
        except Exception as e:
            error_msg = f"通过配置ID发送通知时出错：{str(e)}"
            logger.error(error_msg)
            return False, error_msg